# coding: utf-8
# Author: Legend奇
# Time： 2020/7/17 下午1:33
import os
import sys
from dataclasses import dataclass, field
from typing import List

import yaml

__all__ = [
    'CONFIG',
    'OplatformConfig',
]

import os


def mkdirs(paths: list, exist_ok=True, mode=0o755):
    """
    创建目录

    参数:
    paths (list): 目录路径列表
    exist_ok (bool, optional): 如果目录已存在是否抛出异常，默认为True
    mode (int, optional): 目录权限，默认为0o755

    """
    for path in paths:
        os.makedirs(path, exist_ok=exist_ok)
        os.chmod(path, mode=mode)


def load_config(path):
    """
       加载配置文件

       参数:
       path (str): 配置文件路径

       返回:
       dict: 配置文件内容

       异常:
       Exception: 如果配置文件不存在

       """
    if not os.path.exists(path):
        raise Exception(f'未找到配置文件: {path}')
    with open(path, 'r', encoding='utf-8') as f:
        content = f.read()
    return yaml.load(content, Loader=yaml.FullLoader)


class BasicConfig:
    def create_folders(self, folders):
        mkdirs(folders)

    def init(self):
        pass


@dataclass()
class LogConfig(BasicConfig):
    level: str
    dir: str
    console: List[str] = field(default_factory=list)

    def __init__(self, **kwargs):
        self.console = kwargs.get('console')
        self.dir = kwargs.get('dir')
        self.level = kwargs.get('level')
        self.init()

    def init(self):
        assert self.console is not None, 'ERROR! log.console is None!'
        self.level = self.level.upper()
        self.create_folders([
            self.dir
        ])


@dataclass()
class IpAddressConfig(BasicConfig):
    ipv4: str
    port: int


@dataclass()
class RedisConfig(IpAddressConfig):
    user: str
    password: str
    max_connections: int
    db: int = 0


@dataclass()
class MysqlConfig(IpAddressConfig):
    user: str
    password: str
    name: str
    max_pool_size: int
    core_pool_size: int

    def init(self):
        assert self.max_pool_size >= self.core_pool_size, 'Config Error! max_pool_size < core_pool_size'


@dataclass()
class WxConfig(BasicConfig):
    appid: str
    app_key: str
    merchant_id: str
    merchant_key: str


@dataclass()
class AgentConfig(BasicConfig):
    appid: str
    app_key: str


@dataclass()
class LogConfig(BasicConfig):
    level: str
    dir: str
    console: List[str] = field(default_factory=list)

    def __init__(self, **kwargs):
        self.console = kwargs.get('console')
        self.dir = kwargs.get('dir')
        self.level = kwargs.get('level')
        self.init()

    def init(self):
        assert self.console is not None, 'ERROR! log.console is None!'
        self.level = self.level.upper()
        self.create_folders([
            self.dir
        ])


@dataclass()
class AIConfig(BasicConfig):
    openai_api_base: str
    openai_api_key: str


CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'conf/config.yaml')
if not os.path.exists(CONFIG_PATH):
    raise Exception(f'未找到配置文件: {CONFIG_PATH}')

_CONFIG_DICT = load_config(CONFIG_PATH)


@dataclass()
class AsgiConfig(BasicConfig):
    max_threads: int


@dataclass()
class WebServerConfig(IpAddressConfig):
    is_https: bool


@dataclass()
class LogConfig(LogConfig):
    running_log_name: str = 'running.log'
    error_log_name: str = 'error.log'
    query_log_name: str = 'query.log'

    def get_config(self):
        log_config = {
            'version': 1,
            'disable_existing_loggers': False,
            'formatters': {
                'verbose': {
                    'format': '[{asctime}][{levelname}][{pathname}:{lineno:d}]: {message}',
                    'datefmt': '%Y-%m-%d %H:%M:%S',
                    'style': '{'
                },
                'exception': {
                    'format': '[{asctime}]: {message}',
                    'datefmt': '%Y-%m-%d %H:%M:%S',
                    'style': '{'
                }
            },
            'handlers': {
                'file': {
                    'level': self.level,
                    'class': 'logging.handlers.TimedRotatingFileHandler',
                    'filename': os.path.join(self.dir, self.running_log_name),
                    'formatter': 'verbose',
                    'encoding': 'utf-8',
                    'when': 'D',
                    'interval': 1,
                    'backupCount': 30
                },
                'error': {
                    'level': 'ERROR',
                    'class': 'logging.handlers.TimedRotatingFileHandler',
                    'filename': os.path.join(self.dir, self.error_log_name),
                    'formatter': 'exception',
                    'encoding': 'utf-8',
                    'when': 'D',
                    'interval': 1,
                    'backupCount': 30
                },
                'query': {
                    'level': 'DEBUG',
                    'class': 'logging.handlers.TimedRotatingFileHandler',
                    'filename': os.path.join(self.dir, self.query_log_name),
                    'formatter': 'verbose',
                    'encoding': 'utf-8',
                    'when': 'D',
                    'interval': 1,
                    'backupCount': 30
                },
                'console': {
                    'level': 'DEBUG',
                    'class': 'logging.StreamHandler',
                    'stream': sys.stdout,
                    'formatter': 'verbose',
                }
            },
            'loggers': {
                self.running_log_name: {
                    'handlers': ['file'],
                    'level': self.level,
                    'propagate': True,
                },
                self.query_log_name: {
                    'handlers': ['error'],
                    'level': 'ERROR',
                    'propagate': True
                },
                # 'django.db.backends': {
                #     'handlers': ['query'],
                #     'level': 'DEBUG',
                #     'propagate': True
                # }
            },
        }

        for key, value in log_config['loggers'].items():
            if key in CONFIG.log.console:
                if 'console' not in value['handlers']:
                    value['handlers'].append('console')

        return log_config


@dataclass()
class CacheConfig(BasicConfig):
    applet: int
    manager: int

@dataclass()
class OplatformConfig(BasicConfig):
    log: LogConfig
    asgi: AsgiConfig
    webserver: WebServerConfig
    mysql: MysqlConfig
    redis: RedisConfig
    wx: WxConfig
    agent: AgentConfig
    ai: AIConfig
    cache: CacheConfig
    base_dir = None

    def init(self):
        for name, attrs in self.__dict__.items():
            if isinstance(attrs, BasicConfig):
                attrs.init()
        self.base_dir = os.path.dirname(os.path.abspath(__file__))


_log = LogConfig(**_CONFIG_DICT['log'])

_asgi = AsgiConfig(
    **_CONFIG_DICT['asgi']
)

_web_server_config = WebServerConfig(
    **_CONFIG_DICT['webserver']
)

_mysql = MysqlConfig(
    **_CONFIG_DICT['mysql']
)

_redis = RedisConfig(
    **_CONFIG_DICT['redis']
)

_wx = WxConfig(
    **_CONFIG_DICT['wx']
)
_agent = AgentConfig(
    **_CONFIG_DICT['agent']
)
_ai = AIConfig(
    **_CONFIG_DICT['ai']
)
_cache = CacheConfig(
    **_CONFIG_DICT['cache']
)

CONFIG = OplatformConfig(
    log=_log,
    asgi=_asgi,
    webserver=_web_server_config,
    mysql=_mysql,
    redis=_redis,
    wx=_wx,
    agent=_agent,
    ai=_ai,
    cache=_cache
)

CONFIG.init()
